/*    file: TcpJsonAVFormat.cpp
 *    desc:
 * 
 * created: 2015-11-22
 *  author: chuanjiang.zh@qq.com
 * company: 
 */

#include "TcpJsonAVFormat.h"
#include "TStringCast.h"
#include "Ffmpeg.h"
#include "TFileUtil.h"
#include "Path.h"
#include "CLog.h"


static int s_cmdID = 1;


std::string getDumpPath()
{
    return comn::Path::join(comn::Path::getWorkDir(), "dump.ps");
}

TcpJsonAVFormat::TcpJsonAVFormat():
    m_totalBytes(),
    m_header(),
    m_headerReady()
{
}

TcpJsonAVFormat::~TcpJsonAVFormat()
{
}

int TcpJsonAVFormat::open(const char* ip, int port)
{
    if (!m_socket.open(SOCK_STREAM))
    {
        return ENOENT;
    }

    m_socket.setReuse();

    int rc = m_socket.connect(ip, port);
    if (rc != 0)
    {
        return rc;
    }

    m_socket.setNonblock(true);
    m_socket.setRecvBufferSize(1024*1024);
    
    
    comn::Path::deleteFile(getDumpPath());

    return rc;
}

void TcpJsonAVFormat::close()
{
    m_socket.close();

    m_totalBytes = 0;
}

bool TcpJsonAVFormat::isOpen()
{
    return m_socket.isOpen();
}

int TcpJsonAVFormat::read(uint8_t *buf, int buf_size)
{
    if (!m_socket.checkReadAndClose(1000 * 10))
    {
        return 0;
    }

    if (!m_headerReady)
    {
        char* data = (char*)&m_header + m_recvSize;
        int toRecv = sizeof(m_header) - m_recvSize;
        int length = m_socket.receive(data, toRecv, 0);
        if (length <= 0)
        {
            return 0;
        }

        m_recvSize += length;

        if (length < toRecv)
        {
            return 0;
        }
        
        m_headerReady = true;
    }

    int toRead = m_header.length + sizeof(m_header) - m_recvSize;
    if (toRead > buf_size)
    {
        toRead = buf_size;
    }

    int length = m_socket.receive((char*)buf, buf_size);
    if (length <= 0)
    {
        return 0;
    }

    m_recvSize += length;
    if (m_recvSize >= (sizeof(m_header) + m_header.length))
    {
        /// should read next frame
        m_headerReady = false;
        m_recvSize = 0;
    }

    if (m_header.tag == av::kMediaCommand)
    {
        length = 0;
    }

    return length;
}

int64_t TcpJsonAVFormat::seek(int64_t offset, int whence)
{
    return offset;
}

AVIOContext* TcpJsonAVFormat::create()
{
    int buffer_size = 1024 * 4;
    unsigned char *buffer = (unsigned char *)av_malloc(buffer_size);

    return avio_alloc_context(buffer, buffer_size, 0, this, 
        ps_read_packet, NULL, ps_seek);
}

int TcpJsonAVFormat::ps_read_packet(void *opaque, uint8_t *buf, int buf_size)
{
    TcpJsonAVFormat* fmt = (TcpJsonAVFormat*)opaque;
    return fmt->read(buf, buf_size);
}

int64_t TcpJsonAVFormat::ps_seek(void *opaque, int64_t offset, int whence)
{
    TcpJsonAVFormat* fmt = (TcpJsonAVFormat*)opaque;
    return offset;
}

int TcpJsonAVFormat::play(const std::string& name)
{
    av::MediaTransportHeader hdr;
    memset(&hdr, 0, sizeof(hdr));
    av::MediaTransport::setCommand(hdr, av::kMediaCmdPlay);

    std::string json;
    hdr.length = json.size();

    m_socket.send((char*)&hdr, sizeof(hdr));

    int rc = 0;
    int len = m_socket.send(json.c_str(), json.size());
    if (len != json.size())
    {
        rc = EIO;
    }
    return rc;
}

int64_t  TcpJsonAVFormat::getTotalBytes()
{
    return m_totalBytes;
}

bool TcpJsonAVFormat::readFrame()
{
    if (m_buffer.size() < sizeof(av::MediaTransportHeader))
    {
        char* buf = (char*)m_buffer.data() + m_buffer.size();
        int size = sizeof(av::MediaTransportHeader) - m_buffer.size();
        int length = m_socket.receive(buf, size, 0);
        if (length < 0)
        {
            return false;
        }

        m_buffer.resize(m_buffer.size() + length);

        if (length != size)
        {
            return false;
        }
    }

    av::MediaTransportHeader* header = (av::MediaTransportHeader*)m_buffer.data();
    char* buf = (char*)m_buffer.data() + m_buffer.size();
    int size = header->length - m_buffer.size();

    int length = m_socket.receive(buf, size, 0);
    if (length < 0)
    {
        return false;
    }

    m_buffer.resize(m_buffer.size() + length);
    if (length != size)
    {
        return false;
    }

    return true;
}
